/**********************封包协议************************
* 													  *
* ID		 包总长度	     其他数据                  * 
*													  *	
* 00 06      00 08           00 00 00 00              *
* 2字节	   2字节	      4字节（可以为其他字节数        *
*													  *		
*包总长度 = ID字节数+包总长度字节数+其他数据字节数        *
******************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/select.h>
#include <time.h>
#include <iostream>

#define PORT 7800
//#define ID_VALUE 0x0218//获取上庄列表
// 自定义交易码
enum OpcodeEx
{
    ID_VALUE = 0x8888 ,              //创建牌桌
    CMSG_GETBANGKERLIST = 0x0218, // 获取上庄列表
    SMSG_GETBANGKERLIST = 0x0218,
    CMSG_CHIPIN = 0x0219, // 下注
    SMSG_CHIPIN = 0x0219,
    SMSG_CURRENTCHIPS = 0x021A,    // 当前下注情况
    CMSG_BANKERTHROWDICE = 0x021B, // 庄家掷骰子
    SMSG_BANKERTHROWDICE = 0x021B,
    SMSG_DICENUMBER = 0x021C,     // 广播骰子数目
    SMSG_BANKERUPDATE = 0x021D,   // 庄家动作更新
    SMSG_LOOKCARDS = 0x021E,      // 亮牌
    SMSG_SENDRESULT = 0x021F,     // 下发结果
    CMSG_APPLYBEBANGKER = 0x0220, // 申请上庄
};
    typedef struct Buf{
        uint16_t ID;
        uint16_t ID_SE;
        uint8_t msglen;
        char msg[1024];}
buf;

using namespace std;
class TcpSocketClient
{
  public:
    TcpSocketClient()
    {

    }
    ~TcpSocketClient() {}
    int TcpconnectInit();
    void Recv_Send();
    void run1();
    void run2();
    /**线程函数必须是静态函数，独立于类的实例存在，生命周期是整个process，
     * 且非静态成员函数都会在参数列表上加一个this指针为参数，就不符合线程函数调用规定**/
    static void *func1(void *arg);
    static void *func2(void *arg);

  private:
    int client_sockfd;
    int len;
    uint16_t *IDsend;
    uint32_t msg;
    pthread_t tid1, tid2;
    struct sockaddr_in remote_addr; //服务器端网络地址结构体
};

/*发送线程函数*/
void *TcpSocketClient::func1(void *arg)
{
    TcpSocketClient *ptr = (TcpSocketClient *)arg;
    ptr->run1(); //将拷贝下来的成员变量指向发送处理函数
}
void TcpSocketClient::run1()
{
    int value;
    buf msgto;
    printf("1.创建牌桌\t2.申请上庄\n");
    scanf("%d", &value);
    if(value==1)
        msgto.ID = ID_VALUE;
    else if(value==2)
        msgto.ID = CMSG_APPLYBEBANGKER;
    printf("输入：\n");
    scanf("%s", msgto.msg);
    msgto.msglen = strlen(msgto.msg);
    msgto.ID_SE = strlen(msgto.msg) + 5;
    printf("ID:%#x\n", msgto.ID);
    printf("ID字节数= %d\n", sizeof(msgto.ID));
    printf("ID字节数= %d\n", msgto.ID_SE);
    len = send(client_sockfd, (char *)&msgto, msgto.ID_SE, 0); //发送给服务器
}

/*接受线程函数*/
void *TcpSocketClient::func2(void *arg)
{
    TcpSocketClient *ptr = (TcpSocketClient *)arg;//将void *arg转换为对象的指针
    ptr->run2();//将拷贝下来的成员变量指向接受处理函数
}
void TcpSocketClient::run2()
{
    IDsend = (uint16_t *)malloc(sizeof(uint16_t) * 2);
    len = recv(client_sockfd, IDsend, BUFSIZ, 0); //接受来自服务器的消息
    printf("received:%d\n", IDsend[1]);
}
int TcpSocketClient::TcpconnectInit()
{
    memset(&remote_addr, 0, sizeof(remote_addr));             //数据初始化--清零
    remote_addr.sin_family = AF_INET;                         //设置为IP通信
    remote_addr.sin_addr.s_addr = inet_addr("192.168.0.193"); //服务器IP地址
    remote_addr.sin_port = htons(PORT);                       //服务器端口号

    /*创建客户端套接字--IPv4协议，面向连接通信，TCP协议*/
    if ((client_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket error");
        return 1;
    }

    // 4.等待链接请求并处理--》阻塞
    socklen_t len = sizeof(remote_addr);
    int ret = connect(client_sockfd, (struct sockaddr *)&remote_addr, len);
    if (ret < 0)
    {
        perror("connect");
        exit(1);
    }
    printf("Connection server successful \n");
}

/*线程启动函数*/
void TcpSocketClient::Recv_Send()
{
    while (1)
    {
        pthread_create(&tid1, NULL, func1, (void *)this); //创建线程
        pthread_create(&tid2, NULL, func2, (void *)this); //创建线程
        pthread_join(tid1, NULL);                         //接合指定线程（先执行线程）
    }

    /*关闭套接字*/
    close(client_sockfd);
}

int main(int argc, char *argv[])
{
    TcpSocketClient Tcpclient;
    Tcpclient.TcpconnectInit();
    Tcpclient.Recv_Send();
    return 0;
}
